﻿using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using OpenIddict.Abstractions;
using OpenIddict.Server.AspNetCore;
using System.Security.Claims;
using YDT_UserService.Domain.Entities;
using YDT_UserService.Infrastructure.Data;

namespace YDT_AuthServices.Controllers
{
    /// <summary>
    /// Token控制器
    /// </summary>
    [Route("connect")]
    [ApiController]
    public class TokenController : ControllerBase
    {
        private readonly UserManager<IdentityUser> UserManager;
        private readonly UserServiceDbContext _authContext;

        public TokenController(UserManager<IdentityUser> userManager,
                                UserServiceDbContext authContext)
        {
            UserManager = userManager;
            _authContext = authContext;
        }


        //1、生成token ---> 根据用户信息+客户端信息生成Token
        [HttpPost("token")]
        public async Task<IActionResult> Exchange()
        {
            // 1、获取客户端请求
            var request = HttpContext.GetOpenIddictServerRequest();

            // 2、生成Token方式【使用用户信息+客户端信息】
            if (request.IsPasswordGrantType())
            {
                return await HandlePasswordGrantType(request);
            }

            return BadRequest(new { error = "unsupported_grant_type" });
        }

        private async Task<IActionResult> HandlePasswordGrantType(OpenIddictRequest request)
        {
            Console.WriteLine($"密码授权请求 - 用户名: {request.Username}, Scope: {request.Scope}");

            #region 1、Identity模块提供
            /*// 1、验证用户名
            var user = await UserManager.FindByNameAsync(request.Username);
            if (user == null)
            {
                Console.WriteLine($"用户不存在: {request.Username}");
                return Unauthorized(new { error = "invalid_username" });
            }

            // 2、验证用户密码
            if (!await UserManager.CheckPasswordAsync(user, request.Password))
            {
                Console.WriteLine($"密码错误: {request.Username}");
                return Unauthorized(new { error = "invalid_password" });
            }

            // 3、创建用户身份
            Console.WriteLine($"用户验证成功: {user.UserName}");
            var claimsPrincipal = await CreateClaimsPrincipalAsync(user, request.Scope);

            // 调试：输出所有声明
            foreach (var claim in claimsPrincipal.Claims)
            {
                Console.WriteLine($"声明: {claim.Type} = {claim.Value}");
            }*/
            #endregion

            #region 2、自定义模块提供
            // 1、验证用户名
            var user = _authContext.Users.Where(u => u.UserName == request.Username).First();
            if (user == null)
            {
                Console.WriteLine($"用户不存在: {request.Username}");
                return Unauthorized(new { error = "invalid_username" });
            }
            // 2、验证用户密码
            var password = _authContext.Users.Where(u => u.PasswordHash == request.Password).First();
            if (password == null)
            {
                Console.WriteLine($"密码错误: {request.Username}");
                return Unauthorized(new { error = "invalid_password" });
            }
            // 3、创建用户身份
            Console.WriteLine($"用户验证成功: {user.UserName}");
            var claimsPrincipal = await CreateClaimsPrincipalAsync(user, request.Scope);

            #endregion

            // 4、生成Token
            return SignIn(claimsPrincipal, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);
        }

        /// <summary>
        /// 创建身份 ClaimsPrincipal
        /// </summary>
        /// <param name="user"></param>
        /// <param name="scope"></param>
        /// <returns></returns>
        private async Task<ClaimsPrincipal> CreateClaimsPrincipalAsync(User user, string scope)
        {
            // 1、创建身份
            var identity = new ClaimsIdentity(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);

            // 2、创建声明
            identity.AddClaim(OpenIddictConstants.Claims.Subject, user.Id.ToString());
            identity.AddClaim(OpenIddictConstants.Claims.Username, user.UserName);
            identity.AddClaim(OpenIddictConstants.Claims.Scope, scope);

            // 添加电话
            if (!string.IsNullOrEmpty(user.UserPhone))
            {
                identity.AddClaim(OpenIddictConstants.Claims.PhoneNumber, user.UserPhone);
            }

            // 添加用户角色
            /*var roles = await UserManager.GetRolesAsync(user);
            foreach (var role in roles)
            {
                identity.AddClaim(OpenIddictConstants.Claims.Role, role);
            }*/

            // 3、创建身份人
            var claimsPrincipal = new ClaimsPrincipal(identity);

            // 4、设置作用域
            var scopes = scope?.Split(' ');
            claimsPrincipal.SetScopes(scopes);

            // 5、关键：设置受众（资源）
            claimsPrincipal.SetResources("ProductService", "OrderService", "PaymentService", "SeckillService"); // 添加这行

            // 设置声明的目标（重要！）[方便再微服务中获取声明中的信息]
            foreach (var claim in claimsPrincipal.Claims)
            {
                claim.SetDestinations(GetDestinations(claim, claimsPrincipal));
            }

            return claimsPrincipal;
        }

        /// <summary>
        /// 创建身份 ClaimsPrincipal
        /// </summary>
        /// <param name="user"></param>
        /// <param name="scope"></param>
        /// <returns></returns>
        private async Task<ClaimsPrincipal> CreateClaimsPrincipalAsync(IdentityUser user, string scope)
        {
            // 1、创建身份
            var identity = new ClaimsIdentity(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);

            // 2、创建声明
            identity.AddClaim(OpenIddictConstants.Claims.Subject, user.Id);
            identity.AddClaim(OpenIddictConstants.Claims.Username, user.UserName);
            identity.AddClaim(OpenIddictConstants.Claims.Scope, scope);

            // 添加邮箱
            if (!string.IsNullOrEmpty(user.Email))
            {
                identity.AddClaim(OpenIddictConstants.Claims.Email, user.Email);
            }

            // 添加用户角色
            var roles = await UserManager.GetRolesAsync(user);
            foreach (var role in roles)
            {
                identity.AddClaim(OpenIddictConstants.Claims.Role, role);
            }

            // 3、创建身份人
            var claimsPrincipal = new ClaimsPrincipal(identity);

            // 设置作用域
            var scopes = scope?.Split(' ');
            claimsPrincipal.SetScopes(scopes);

            // 4、关键：设置受众（资源）
            //claimsPrincipal.SetResources("api"); // 添加这行

            // 设置声明的目标（重要！）[方便再微服务中获取声明中的信息]
            foreach (var claim in claimsPrincipal.Claims)
            {
                claim.SetDestinations(GetDestinations(claim, claimsPrincipal));
            }

            return claimsPrincipal;
        }

        // 添加这个方法来确定声明应该出现在哪里
        private static IEnumerable<string> GetDestinations(Claim claim, ClaimsPrincipal principal)
        {
            switch (claim.Type)
            {
                case OpenIddictConstants.Claims.Name:
                case ClaimTypes.Name:
                    yield return OpenIddictConstants.Destinations.AccessToken;
                    if (principal.HasScope(OpenIddictConstants.Scopes.Profile))
                        yield return OpenIddictConstants.Destinations.IdentityToken;
                    break;

                case OpenIddictConstants.Claims.Email:
                case ClaimTypes.Email:
                    yield return OpenIddictConstants.Destinations.AccessToken;
                    if (principal.HasScope(OpenIddictConstants.Scopes.Email))
                        yield return OpenIddictConstants.Destinations.IdentityToken;
                    break;

                case OpenIddictConstants.Claims.Role:
                case ClaimTypes.Role:
                    yield return OpenIddictConstants.Destinations.AccessToken;
                    if (principal.HasScope(OpenIddictConstants.Scopes.Roles))
                        yield return OpenIddictConstants.Destinations.IdentityToken;
                    break;

                case OpenIddictConstants.Claims.Username:
                    yield return OpenIddictConstants.Destinations.AccessToken;
                    break;

                default:
                    yield return OpenIddictConstants.Destinations.AccessToken;
                    break;
            }
        }
    }
}
